package whendone

/**
  Filtering of projects.
  @author Henrik Huttunen
*/
object Filtering{
  import Helpers._
	import PathFiltering._
  
  /** Filter criteria for Either filter is exactly part.name or contains it, depending if filter has mark " around it */
  def nameFilter(part: Project, filter: String) = {
    // if project's name must be exactly 'filter'
    if(filter.startsWith("\"") & endsWith(filter, "\"") && filter.size > 2)
      part.name == filter.substring(1, filter.size - 1)
    else part.name.contains(filter)   
  }
  /** Filter criteria for a single project. */
  def pathFilter(part: Project, filter: String) = {
    // whether filter is form of a/b
    filter.filter(_ == '/').size match {
      case 0 => nameFilter(part, filter)
      case 1 => singlePartMatch(part, filter) || twoPartsMatch(part, filter)
      case _ => pathMatch(part, filter) 
      //ALTERNATIVE nameChain(part) contains (filter) // something like this might work?
    }
  }
  /** Filter for a single atrribute 
      precondition: filter starts with "@" */
  val attributeFilter = {(part: Project, filter: String) => {
    part.attributes.contains(filter)
  }}
  /** returns (partA, partB), x belongs to partA <=> x starts with mark */
  def partitionFilters(filters: List[String], mark: String) = {
    val (a, b) = filters partition (_ startsWith(mark) )
    (a map (x => x substring(1, x.size)), b)
  }
  /** Returns projects which satisfy filters. */
  def filterContains(project: Project, filters: List[String]): List[Project] = {    
    val (negative, positive) = partitionFilters(filters, "!")
    allProjects(project) filter (x => satisfyPositiveFilters(x, positive) && (satisfyNegativeFilters(x, negative))) removeDuplicates    
  }
  /** Filters away paths that contain any part of any filter. */    
  def satisfyNegativeFilters(project: Project, filters: List[String]): Boolean = {
    filters.isEmpty || {
      val (attributeFilters, pathFilters) = partitionFilters(filters, "@")
      !project2pathchain(project).exists(path => pathFilters.exists(pathFilter(path, _))) &&
      !attributeFilters.exists(attributeFilter(project, _))
    }
  }
  /** Filters away projects that doesn't satisfy all the attribute filters, and also all satisfy path filters in some part of the path of the project.
      e.g. if filters are @major dum huh, then project must have attribute @major, and dum and huh must be satisfied by union of the names of project and its grandparents. 
   */
  def satisfyPositiveFilters(project: Project, filters: List[String]): Boolean = {
    filters.isEmpty || {
      val (attributeFilters, pathFilters) = partitionFilters(filters, "@")
      if(attributeFilters.isEmpty){
        // there must be a filter is matched for project's path prefix, 
        // because otherwise a (grand)parent would satisfy exactly the same, and we got redundancy
        pathFilters.find(pathFilter(project, _)) match {
          case None => false
          // the rest of the filters must also be satisfied (includes the found) 
          case _ => pathFilters.forall(filter => project2pathchain(project).exists(pathFilter(_, filter)))
        }
      }else{
        (attributeFilters.forall(attributeFilter(project, _)) &&
        (pathFilters.isEmpty || pathFilters.forall(filter => project2pathchain(project).exists(pathFilter(_, filter)))))
      }
    }
  }
}